/* search.c, Ait, BSD 3-Clause, Kevin Bloom, 2023,
   Derived from: Atto January 2017
   Derived from: Anthony's Editor January 93
*/

#include "header.h"
#include "termbox.h"

#define FWD_SEARCH 1
#define REV_SEARCH 2

void search_fwd()
{
  search(FWD_SEARCH);
}

void search_rev()
{
  search(REV_SEARCH);
}


void search(int initdir)
{
  char *prompt = "Search: ";
  int cpos = 0;
  int c, dir = initdir;
  point_t o_point = curbp->b_point;
  point_t found = o_point;
  int start_col = strlen(prompt);
  struct tb_event ev;
  char prev_search[STRBUF_M];
  strcpy(prev_search, searchtext);
//  searchtext[0] = '\0';
  memset(searchtext, 0, STRBUF_M);
  display_prompt_and_response(prompt, searchtext);
  cpos = strlen(searchtext);

  for (;;) {
    tb_present();
    if(tb_poll_event(&ev) != TB_OK) return;

    if(msgline_editor(ev, prompt, searchtext, &cpos))
      continue;

    if(!ev.mod)
      c = ev.ch;
    else
      c = ev.key;

    /* ignore control keys other than return, C-g, CR,  C-s, C-R, ESC */
    if (c < 32 &&
        c != TB_KEY_CTRL_G &&
        c != TB_KEY_ENTER &&
        c != TB_KEY_CTRL_R &&
        c != TB_KEY_CTRL_S &&
        c != TB_KEY_ESC)
      continue;

    switch(c) {
    case TB_KEY_ENTER: /* return */
      if(found != o_point) {
        found_point = -1;
        return;
      }
      found = search_forward(curbp, curbp->b_point, searchtext);
      display_search_result(found, FWD_SEARCH, prompt, searchtext);
      break;

    case TB_KEY_ESC: /* esc */
    case TB_KEY_CTRL_G: /* ctrl-g */
      found_point = -1;
      curbp->b_point = o_point;
      return;

    case TB_KEY_CTRL_S: /* ctrl-s, do the search */
      if(searchtext[0] == '\0') {
        strcpy(searchtext, prev_search);
        cpos = strlen(searchtext);
      }
      found = search_forward(curbp, curbp->b_point, searchtext);
      display_search_result(found, FWD_SEARCH, prompt, searchtext);
      dir = FWD_SEARCH;
      break;

    case TB_KEY_CTRL_R: /* ctrl-r, do the search */
      if(searchtext[0] == '\0') {
        strcpy(searchtext, prev_search);
        cpos = strlen(searchtext);
      }
      found = search_backwards(curbp, curbp->b_point-1, searchtext);
      display_search_result(found, REV_SEARCH, prompt, searchtext);
      dir = REV_SEARCH;
      break;

    case TB_KEY_BACKSPACE2: /* del, erase */
    case TB_KEY_BACKSPACE: /* backspace */
      if (cpos == 0)
        continue;
      searchtext[--cpos] = '\0';
      tb_set_cursor(start_col + cpos, MSGLINE);
      display_prompt_and_response(prompt, searchtext);
      break;

    default:
      if (cpos < STRBUF_M - 1) {
        for(int i = strlen(searchtext); searchtext[i] != '\0'; i--) {
          searchtext[i] = searchtext[i - 1];
        }
        searchtext[cpos] = c;
        tb_set_cursor(start_col, MSGLINE);
        addstr(searchtext);
        cpos++;
        tb_set_cursor(start_col + cpos, MSGLINE);
        if(dir == FWD_SEARCH)
          found = search_forward(curbp, curbp->b_point-strlen(searchtext), searchtext);
        else
          found = search_backwards(curbp, curbp->b_point, searchtext);
        display_search_result(found, dir, prompt, searchtext);
      }
      break;
    }
  }
}

void display_search_result(point_t found, int dir, char *prompt, char *search)
{
  int i = curwp->w_rows / 2, found_end = FALSE, shift = 0;
  point_t new_page = 0;

  if (found != -1 ) {
    search_dir = dir;
    tb_clear();
    found_point = found;
    curbp->b_point = dir == 2 ? found + 1 : found;
    update_display(TRUE);
    msg("%s%s",prompt, search);
    new_page = curbp->b_page;
    shift = curwp->w_row - i;
    if(dir == FWD_SEARCH) {
      for(int k = shift; k > 0; k--) {
        found_end = FALSE;
        while(found_end == FALSE) {
          if(*ptr(curbp, new_page) == '\n')
            found_end = TRUE;
          new_page++;
        }
      }
    } else {
      for(int k = shift; k < 0; k++) {
        new_page = lnstart(curbp,new_page - 1);
      }
    }
    curbp->b_page = lnstart(curbp, new_page);
    update_display(TRUE);
  } else {
    found_point = -1;
    msg("Failing %s%s",prompt, search);
    dispmsg();
    curbp->b_point = (dir == FWD_SEARCH ? 0 : pos(curbp, curbp->b_ebuf));
  }
}

point_t search_forward(buffer_t *bp, point_t start_p, char *stext)
{
  point_t end_p = pos(bp, bp->b_ebuf);
  point_t p,pp;
  char* s;
  char_t * cur;
  int case_sense = FALSE;

  if (0 == strlen(stext))
    return start_p;

  for (p=start_p; p < end_p; p++) {
    for (s=stext, pp=p;  *s !='\0' && pp < end_p; s++, pp++) {
      cur = ptr(bp, pp);
      if(isupper(*s)) {
        case_sense = TRUE;
        if(*s != *cur)
          break;
      }
      else if (*s != *cur && case_sense)
        break;
      else if(*s != tolower(*cur))
        break;
    }

    if (*s == '\0')
      return pp;
  }

  return -1;
}

point_t search_backwards(buffer_t *bp, point_t start_p, char *stext)
{
  point_t p,pp;
  char* s;
  char_t * cur;
  int case_sense = FALSE;

  if (0 == strlen(stext))
    return start_p;

  for (p=start_p; p >= 0; p--) {
    for (s=stext, pp=p; *s != '\0' && pp > -1; s++, pp++) {
      cur = ptr(bp, pp);
      if(isupper(*s)) {
        case_sense = TRUE;
        if(*s != *cur)
          break;
      }
      else if (*s != *cur && case_sense)
        break;
      else if(*s != tolower(*cur))
        break;
    }

    if (*s == '\0') {
      if (p > 0)
        p--;
      return p;
    }
  }
  return -1;
}
